////////////////////////////////////////////////////////////////////
// Gestion de la copie de CD et création d'image pour Chrono Cross
////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////
// Nemesis pour Terminus Traduction (tt_nemesis@yahoo.fr)
// Date de création :		27/05/04
// Dernière modification :	27/05/04
////////////////////////////////////////////////////////////////////

//*************************************************************
// 22 July 2003
// Mainly written by Truman (credits go to him)
// 
// 11 May 2004
// adapted by Nemesis
//*************************************************************

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>
#include "my_ntddscsi.h"
#include "Patcheur.h"

HANDLE hVolume, hFile;
DWORD nb_sectors = 0;
DWORD dwNotUsed;

SCSI_PASS_THROUGH_DIRECT sptd;
byte buffer[SECTEUR_TOTAL]; //buffer for holding sector data from drive
byte senseBuf[255]; //buffer for holding sense data from drive (not used in this example)

/*
 * Class:     ImageCD
 * Method:    imageCD_nbSectors
 * Signature: ()I
 *
 * Number of sectors on the CD
 */
JNIEXPORT jint JNICALL Java_Librairie_imageCD_1nbSectors
  (JNIEnv *env, jclass classe)
{
	return nb_sectors;
}

/*
 * Class:     ImageCD
 * Method:    imageCD_testDrive
 * Signature: (Ljava/lang/String;Ljava/lang/String;)I
 *
 * Test if a drive is a CD-Rom drive (1 = true)
 */
JNIEXPORT jint JNICALL Java_Librairie_imageCD_1testDrive
	(JNIEnv *env, jclass classe, jchar s)
{
	char cd_path[100];
	sprintf(cd_path,"%c:\\",s);

	// Not a CD-Drive
    if (GetDriveType(cd_path)!=DRIVE_CDROM) {        
		CloseHandle (hFile);
        return ERROR_DRIVE;
	} else
		return ERROR_NONE;
}

/*
 * Class:     ImageCD
 * Method:    imageCD_close
 * Signature: (Ljava/lang/String;Ljava/lang/String;)I
 *
 * Close files and unlock the drive
 */
JNIEXPORT jint JNICALL Java_Librairie_imageCD_1close
	(JNIEnv *env, jclass classe)
{
	// Unlock the disc in the CD-ROM drive.
	PREVENT_MEDIA_REMOVAL pmrLockCDROM;
	pmrLockCDROM.PreventMediaRemoval = FALSE;
	DeviceIoControl (hVolume, IOCTL_DISK_MEDIA_REMOVAL,
				   &pmrLockCDROM, sizeof(pmrLockCDROM), NULL,
				   0, &dwNotUsed, NULL);

	CloseHandle (hVolume);
	CloseHandle (hFile);

	return 0;
}

/*
 * Class:     ImageCD
 * Method:    imageCD_init
 * Signature: (Ljava/lang/String;Ljava/lang/String;)I
 *
 * Open the files and initialize the variables
 */
JNIEXPORT jint JNICALL Java_Librairie_imageCD_1init
	(JNIEnv *env, jclass classe, jstring s, jstring d)
{
	// Build C++ strings from Java objects
	const char *source = env->GetStringUTFChars(s,0);
	const char *dest = env->GetStringUTFChars(d,0);
	
	// Creation of the .cue file
	char *destcue = strdup(dest);
	char *destname = strdup(dest);
	strcpy(&destcue[strlen(destcue)-3],"cue");
	FILE *cue = fopen(destcue,"w");
	// Retrieval of the path in "dest"
	char *token = strtok(destname,"\\/");
	char *destsauv = destname;
	while (token!=NULL) {
		destsauv = token;
		token = strtok(NULL,"\\/");
	}
	// Copy of data
	fprintf(cue,"FILE \"%s\" BINARY\n",destsauv);
	fprintf(cue,"  TRACK 01 MODE2/2352\n");
    fprintf(cue,"INDEX 01 00:00:00");
	fclose(cue);

	// CD-ROM
	char cd_path[100];
	sprintf(cd_path,"%s:\\",source);

	// Test the drive
    if (GetDriveType(cd_path)!=DRIVE_CDROM) {        
		CloseHandle (hFile);
        return ERROR_DRIVE;
	}
    
	// Get size of the disk in sectors
	GetDiskFreeSpace(cd_path, &dwNotUsed,
		&dwNotUsed, &dwNotUsed, &nb_sectors);

	// Ouverture du CD
	sprintf(cd_path,"\\\\.\\%s:",source);
    hVolume = CreateFile(cd_path, GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

	// Impossible to "open" the CD
    if (hVolume == INVALID_HANDLE_VALUE
		|| nb_sectors==0) {
		CloseHandle (hFile);
		return ERROR_CD;
	}

	// Configure the future reading commands
    sptd.Length=sizeof(sptd);
    sptd.PathId=0;   //SCSI card ID will be filled in automatically
    sptd.TargetId=0; //SCSI target ID will also be filled in
    sptd.Lun=0;      //SCSI lun ID will also be filled in
    sptd.CdbLength=12; //CDB size is 12 for ReadCD MMC1 command
    sptd.SenseInfoLength=0; //Don't return any sense data
    sptd.DataIn=SCSI_IOCTL_DATA_IN; //There will be data from drive
    sptd.DataTransferLength=SECTEUR_TOTAL; //Size of data - in this case 1 sector of data
    sptd.TimeOutValue=5;  //SCSI timeout value (5 seconds - maybe it should be longer)
    sptd.DataBuffer=(PVOID)&(buffer);
    sptd.SenseInfoOffset=0;

    // CDB with values for ReadCD CDB12 command.
	// The values were taken from MMC1 draft paper.
    sptd.Cdb[0]=0xBE;  //Code for ReadCD12 command
    sptd.Cdb[1]=0;
	memset(&sptd.Cdb[2], 0, 4 );
    sptd.Cdb[6]=0;  //No. of sectors to read from CD byte 2 (MSB)
    sptd.Cdb[7]=0;  //No. of sectors to read from CD byte 1
    sptd.Cdb[8]=1;  //No. of sectors to read from CD byte 0 (LSB)
    sptd.Cdb[9]=0xF8;  //Raw read, 2352 bytes per sector
    sptd.Cdb[10]=0;  //Sub-channel selection bits.
    sptd.Cdb[11]=0;
    sptd.Cdb[12]=0;
    sptd.Cdb[13]=0;
    sptd.Cdb[14]=0;
    sptd.Cdb[15]=0;

	// Image file
	hFile = CreateFile (dest,
				   GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
				   FILE_ATTRIBUTE_NORMAL, NULL);

	// Impossible to open the file
	if (hFile == INVALID_HANDLE_VALUE)
		return ERROR_FILE;

	// Everything is ok
	return ERROR_NONE;
}

/* Write the sector number "s" in "sptd" */
void writeSectorNb(long s)
{
	for(int i=5; i>1; i--) {
		sptd.Cdb[i] = s & 0xFF;
		s >>= 8;
	}
}

/*
 * Class:     ImageCD
 * Method:    imageCD_readSector
 * Signature: (Ljava/lang/String;Ljava/lang/String;)I
 *
 * Read a CD sector
 */
JNIEXPORT jint JNICALL Java_Librairie_imageCD_1readSector
	(JNIEnv *env, jclass classe, jint sector)
{
    if (hVolume == INVALID_HANDLE_VALUE)	
		return ERROR_CD;

    if (sector<0 || sector>(signed)nb_sectors)	
		return ERROR_SECTOR;

	// Sector put in sptd
	writeSectorNb(sector);

    //Send the command to drive
    if (DeviceIoControl(
		hVolume, IOCTL_SCSI_PASS_THROUGH_DIRECT,               
        (PVOID)&sptd, (DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT),
        NULL, 0, &dwNotUsed, NULL))
	{
		WriteFile (hFile, buffer, SECTEUR_TOTAL, &dwNotUsed, NULL);
    }
	return ERROR_NONE;
}